package com.yl.demo.base.filter;

import com.yl.demo.model.UserInfo;
import com.yl.demo.utils.BaseUtil;
import com.yl.demo.utils.CookiesUtil;
import com.yl.demo.utils.Result;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.io.Serializable;

/**
 * Created by YL on 2019/10/17.
 * 前后端分离项目中，由于跨域，会导致复杂请求，即会发送preflighted request，
 * 这样会导致在GET／POST等请求之前会先发一个OPTIONS请求，但OPTIONS请求并不带shiro的'Authorization'字段
 * （shiro的Session），即OPTIONS请求不能通过shiro验证，会返回未认证的信息。
 * 解决方法：给shiro增加一个过滤器，过滤OPTIONS请求
 * isAccessAllowed：表示是否允许访问；mappedValue就是[urls]配置中拦截器参数部分，如果允许访问返回true，否则false；
 * onAccessDenied：表示当访问拒绝时是否已经处理了；如果返回true表示需要继续处理；如果返回false表示该拦截器实例已经处理了，将直接返回即可。
 * onPreHandle：会自动调用这两个方法决定是否继续处理；
 */
public class CORSAuthenticationFilter extends FormAuthenticationFilter {

    private final static Logger logger = LoggerFactory.getLogger(CORSAuthenticationFilter.class);

    private final String X_AUTH_TOKEN = "authorizationtoken"; // AuthorizationToken x-auth-token

    public CORSAuthenticationFilter() {
        super();
    }

    @Override
    public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        //Always return true if the request's method is OPTIONSif (request instanceof HttpServletRequest) {
        if (((HttpServletRequest) request).getMethod().toUpperCase().equals("OPTIONS")) {
            return true;
        }
        return super.isAccessAllowed(request, response, mappedValue);
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest req = (HttpServletRequest)request;
        HttpServletResponse resp = (HttpServletResponse) response;
        Subject subject = getSubject(req, resp);
        if(!subject.isAuthenticated() && !subject.isRemembered()) {
            //如果没有登录，直接进行之后的流程
            return true;
        }
        // 判断是否存在sessionId 存在则返回true
      /*    String id = WebUtils.toHttp(req).getHeader(X_AUTH_TOKEN);
       if (StringUtils.isNotBlank(id)){
            // 验证sessionID是否有效
            try{
                Cookie sessionCookie = (Cookie) CookiesUtil.getCookieByName(req, this.X_AUTH_TOKEN);
                if (sessionCookie != null) {
                    String sessionOldId = sessionCookie.getValue();
                    logger.info("cookie sessionOldId =={"+sessionOldId+"} request sessionReqId =={"+id+"}");
                    if (StringUtils.equals(sessionOldId, id)){
                        logger.info("User sessionId There is the head");
                        return true;
                    }else{
                        this.valid(req, resp);
                    }
                }else{
                    this.valid(req, resp);
                }
            }catch (Exception e){
                this.valid(req, resp);
                e.printStackTrace();
            }
        }*/
        // If the subject isn't identified, redirect to login URL
        // 如果没有标识主题，则重定向到登录URL
       /* if (subject.getPrincipal() == null) {
            logger.info("User not logged in, Please login again");
            this.valid(req, resp);
            // saveRequestAndRedirectToLogin(request, response);
        }else {
            this.valid(req, resp);
        }*/
        this.valid(req, resp);
        return false;
    }

    public boolean valid(HttpServletRequest req, HttpServletResponse resp){
        try {
            logger.info("User not logged in, Please login again");
            resp.setHeader("Access-Control-Allow-Origin",  req.getHeader("Origin"));
            resp.setHeader("Access-Control-Allow-Credentials", "true");
            resp.setContentType("application/json; charset=utf-8");
            resp.setCharacterEncoding("UTF-8");
            PrintWriter out = resp.getWriter();
            Result result = new Result(20099, "User not logged in");
            out.println(BaseUtil.toJSON(result));
            out.flush();
            out.close();
        }catch (Exception e){
            e.printStackTrace();
        }
        return false;
    }

}
